#ifndef ___fuse_write
#define ___fuse_write 1

#ifndef FUSE_USE_VERSION
#define FUSE_USE_VERSION 28
#endif

#include <stdio.h>
#include <stdlib.h>
#include "mtlib.c"
#include <fuse.h>
#include <fcntl.h>
#include <errno.h>
#include "logging.c"

struct datosEscribir {
	long long descriptor;
	char * buffer;
	long cantidad;
	char estado;
	long posicion;
};

//PROTOTIPOS
int fuse_write(const char *, const char *, size_t, off_t, struct fuse_file_info *);
void ___fuse_procesar_mensaje_write(unsigned char, mti_mensaje, struct datosEscribir *);

//ESCRIBIR DATOS EN EL ARCHIVO
int fuse_write(const char * path, const char * buffer, size_t size, off_t offset, struct fuse_file_info * ptr) {

	int resultado, resultado2, resultado3;
	mti_mensaje datosMensaje;
	uint64_t descriptor;
	long cantidadPartes, numeroParte, cantidad, antCantidad, tamanoDatos;
	unsigned char centro_write = 0;
	unsigned int descriptorConexion, parar = 0;
	unsigned int parar2 = 0;
	struct datosEscribir datos;

		descriptor = ptr -> fh;
		cantidadPartes = 1;

		datos.estado = 0;
		datos.descriptor = (long)descriptor;
		datos.cantidad = (long)size;
		datos.buffer = buffer;
		datos.posicion = (long)offset;

		//si el size de los datos del buffer supera los 65535bytes lo mando por partes
		while ( (datos.cantidad) > 65534) {

			antCantidad = datos.cantidad;
			datos.cantidad = (datos.cantidad) - 20000;
			cantidadPartes ++;

		}

		resultado = mtf_enviar_mensaje(centro_write, descriptorConexion, MTE_ESCRIBIR_ARCHIVO,(long long) datos.descriptor, (long) datos.cantidad,
										(long) datos.posicion, (long) cantidadPartes);

		  if (resultado == 0) {
		    mtf_desconectar(centro_write, descriptorConexion);
		    mtf_liberar_descriptor(centro_write);
		    return -EAGAIN;
		  }

		numeroParte = 1;

		log_registrar(descArch, "FUSE", LOG_INFO, "Descriptor, Tamano, Posicion, CantidadPartes %lld %ld %ld %ld \n", datos.descriptor, datos.cantidad,
						datos.posicion, cantidadPartes);

		while (parar == 0) {

			if (mtf_recibir_mensajes(centro_write) == 0) continue;

			mti_mensaje datosMensaje = mtf_siguiente_mensaje(centro_write);

			if (datosMensaje.remitente != descriptorConexion) {
			mtf_descartar_mensaje(centro_write, datosMensaje.codigo);
			continue;
			}

			if (datosMensaje.categoria != MTA_MENSAJE) {
			continue;
			}

			if (datosMensaje.tipo == MTR_PUEDE_ESCRIBIR_ARCH) {
			return parar = 1;
			}
		}

		//Mando los datos a escribir cada 20k
		while(numeroParte <= cantidadPartes) {

				if (numeroParte == 1) {

					tamanoDatos = 0;
					tamanoDatos = datos.cantidad;

				} else {

					tamanoDatos = 0;
					tamanoDatos = (antCantidad - datos.cantidad);

					}

				numeroParte ++;

				resultado2 = mtf_enviar_mensaje(centro_write, descriptorConexion, MTE_DATOS_ESCRIBIR_ARCH, (long long ) datos.descriptor,(long)
												numeroParte, (int) tamanoDatos, (void *) datos.buffer);
												
				log_registrar(descArch, "FUSE", LOG_INFO, "Descriptor, NumeroParte, tamanoDatos %lld %ld %ld \n",
								        datos.descriptor, numeroParte, tamanoDatos);


				  if (resultado2 == 0) {
				    mtf_desconectar(centro_write, descriptorConexion);
				    mtf_liberar_descriptor(centro_write);
				    return -EAGAIN;

				  } else
					  while (parar2 == 0) {

						  if (mtf_recibir_mensajes(centro_write) == 0) continue;

						  mti_mensaje datosMensaje = mtf_siguiente_mensaje(centro_write);

						  if (datosMensaje.remitente != descriptorConexion) {
							  mtf_descartar_mensaje(centro_write, datosMensaje.codigo);
							  continue;
						  }

						  if (datosMensaje.categoria != MTA_MENSAJE) {
							  continue;
						  }

						  if (datosMensaje.tipo == MTR_RECIBIO_DATOS_ARCH) {
							  return parar2 = 1;
						  }
					  	}
		}

		resultado3 = mtf_enviar_mensaje(centro_write, descriptorConexion, MTE_FIN_DATOS_ESCRIBIR, (long long) datos.descriptor);

						log_registrar(descArch, "FUSE", LOG_INFO, "Se enviaron todos los datos para escribir, descriptor %lld \n",
								        datos.descriptor);

		  if (resultado3 == 0) {
		    mtf_desconectar(centro_write, descriptorConexion);
		    mtf_liberar_descriptor(centro_write);
		    return -EAGAIN;

		  }

		  while (!datos.estado) {

		    if (!mtf_recibir_mensajes(centro_write)) continue;

		    datosMensaje = mtf_siguiente_mensaje(centro_write);
		    ___fuse_procesar_mensaje_write(centro_write, datosMensaje, &datos);
		    mtf_descartar_mensaje(centro_write, datosMensaje.codigo);

		  }

		  mtf_desconectar(centro_write, descriptorConexion); //desconecto la conexion del centro de mensajes
		  mtf_liberar_descriptor(centro_write); //libero centro de mensajes con sus datos y conexiones

		  switch (datos.estado) {
		    // el estado es 255 si no hay error, o el motivo del error si lo hay
		    case 1:
		    	return -EBADF; //el descriptor no existe
		    case 2:
		    	return -EBADF; //el archivo no existe
		    case 3: 
                return -EACCES; //disco lleno
            case 4:
                return -EFBIG; //pos de escritura mas alla del fin de archivo
		    default:
		    	return datos.cantidad; //retorno la cantidad de datos escritos

		  }
}

void ___fuse_procesar_mensaje_write(unsigned char centro_write, mti_mensaje datosMensaje, struct datosEscribir * datosEscritura) {


	if (datosMensaje.categoria != MTA_MENSAJE) return;

	  switch (datosMensaje.tipo) {

	    case MTE_NULL:

	      mtf_enviar_mensaje(centro_write, datosMensaje.remitente, MTR_NULL);

	    break;

	    case MTR_NULL: case MTQ_MENSAJE_NO_VALIDO:

	    break;

		//MENSAJES PARA ESCRIBIR DATOS
		case MTR_NO_ESCRIBIO_ARCHIVO:
		{
			long long descriptor;
			char motivo;

				mtf_obtener_datos(centro_write, datosMensaje.codigo,(long long **) &descriptor, (char *) &motivo);

				log_registrar(descArch, "FUSE", LOG_WARN,"No se escribieron datos %lld \n", descriptor);
				datosEscritura -> estado = motivo;
		}
		break;

		case MTR_ESCRIBIO_EN_ARCHIVO:
		{
			long cantidad;
			long long descriptor;

				mtf_obtener_datos(centro_write, datosMensaje.codigo,(long long **) &descriptor,(long int *) &cantidad);

				log_registrar(descArch, "FUSE", LOG_INFO, "Descriptor, Cantidad de datos escritos %lld %ld \n", descriptor, cantidad);
				datosEscritura -> cantidad = cantidad;  //retorno la cantidad de bytes escritos
				datosEscritura -> estado = 255;
		}
		break;

		//si me llega cualquier cosa lo mando de vuelta
		default:

				mtf_enviar_mensaje(centro_write, datosMensaje.remitente, MTQ_MENSAJE_NO_VALIDO);

		break;
	  }

}

#endif
